package com.zksite.nator.server.http.handler;

import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.zksite.nator.common.constants.Constants;
import com.zksite.nator.common.container.ChannelContainer;
import com.zksite.nator.common.exception.PingTimeoutException;
import com.zksite.nator.common.processor.RequestProcessor;
import com.zksite.nator.processor.http.container.DefaultClientContainer;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

@Component
@Scope("prototype")
public class ForwardServerHandle extends ChannelInboundHandlerAdapter {

	private static final Logger LOGGER = LoggerFactory.getLogger(ForwardServerHandle.class);

	@Autowired
	private DefaultClientContainer clientContainer;

	@Resource(name = "sessionContainer")
	private ChannelContainer sessionContainer;

	@Resource(name = "internalRequestProcessor")
	private RequestProcessor internalRequestProcessor;

	private static final String FORWARID_HEAD = "forwardid";

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if (msg instanceof FullHttpResponse) {
			FullHttpResponse response = (FullHttpResponse) msg;
			HttpHeaders headers = response.headers();
			String messageType = headers.get(Constants.MESSAGE_TYPE);
			if (messageType != null) {// 有自定义头，内部消息通讯
				internalRequestProcessor.process(ctx, msg);
			} else {
				String forwardid = headers.get(FORWARID_HEAD);
				if (forwardid == null) {
					return;
				}
				Channel channel = sessionContainer.get(forwardid);
				if (channel != null && channel.isActive()) {
					headers.remove(FORWARID_HEAD);
					channel.writeAndFlush(response);
					sessionContainer.remove(forwardid);
				}
			}

		}
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
		LOGGER.info("new client to connect,host:{},port:{}", insocket.getAddress(), insocket.getPort());
		sendMappingRequest(ctx.channel());
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
		LOGGER.info("a client disconnect,host:{},port:{}", insocket.getHostName(), insocket.getPort());
		clientContainer.closeChannel(ctx.channel());
	}

	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		if (evt instanceof IdleStateEvent) {
			InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
			IdleStateEvent event = (IdleStateEvent) evt;
			if (event.state() == IdleState.WRITER_IDLE) {
				LOGGER.debug("send ping message.host:{}", insocket.getAddress());
				HttpRequest httpRequest = creatPingRequest();
				if (httpRequest != null) {
					if (ctx.channel().isActive()) {
						ctx.writeAndFlush(httpRequest);
					}
				} else {
					LOGGER.error("create ping message error");
				}
			} else if (event.state() == IdleState.READER_IDLE) {
				LOGGER.warn("ping client fail.host:{} ", insocket.getAddress());
				throw new PingTimeoutException("ping timeout");
			}
		} else {
			super.userEventTriggered(ctx, evt);
		}
	}

	/**
	 * 创建ping请求
	 * 
	 * @return
	 */
	private HttpRequest creatPingRequest() {
		URI uri;
		try {
			uri = new URI("http://127.0.0.1");
			HttpRequest ping = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toASCIIString());
			ping.headers().add(Constants.MESSAGE_TYPE, Constants.MESSAGE_PING);
			return ping;
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 发送映射请求
	 * 
	 * @param channel
	 */
	private void sendMappingRequest(Channel channel) {
		URI uri;
		try {
			uri = new URI("http://127.0.0.1");
			HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toASCIIString());
			request.headers().add(Constants.MESSAGE_TYPE, Constants.MESSAGE_REQUEST_MAPPING);
			channel.writeAndFlush(request);
			InetSocketAddress insocket = (InetSocketAddress) channel.remoteAddress();
			LOGGER.info("send request mapping message.host:{}", insocket.getAddress());
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}

	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		if (cause instanceof PingTimeoutException) {
			ctx.close();
		}
	}

}
